#include "IPAddress.h"

#include <string.h> //memset

SIPAddress SIPAddress::LocalHostIPv4 = SIPAddress(127,0,0,1);

TOptional<SIPAddress> SIPAddress::Create(const CString &InIP)
{
    if (InIP==U"localhost")
    {
        return LocalHostIPv4;
    }

    //check if it is ipv6
    if (InIP.Find(U':') !=-1)
    {
        //ipv6
        //split the string
        auto IPArray = InIP.Split(U':');

        //check if it is a valid ipv6 address
        if (IPArray.Num() != 8)
        {
            //invalid ipv6 address
            return TOptional<SIPAddress>();
        }

        //convert to ipv6 address
        uint16_t IPv6[8];
        for (int i = 0; i < 8; i++)
        {
            if(!IPArray[i].IsHex())
            {
                //invalid ipv6 address
                return TOptional<SIPAddress>();
            }
            IPv6[i] = (uint16_t)IPArray[i].HexToUInt64();
        }

        return SIPAddress(IPv6[0], IPv6[1], IPv6[2], IPv6[3], IPv6[4], IPv6[5], IPv6[6], IPv6[7]);
    }
    else
    {
        //ipv4
        //split the string
        auto IPArray = InIP.Split(U'.');
        if (IPArray.Num() != 4)
        {
            //invalid ipv4 address
            return TOptional<SIPAddress>();
        }

        //convert to ipv4 address
        uint8_t IPv4[4];
        for (int i = 0; i < 4; i++)
        {
            if(IPArray[i].IsDigit())
            {
                //invalid ipv4 address
                return TOptional<SIPAddress>();
            }
            IPv4[i] = (uint8_t)IPArray[i].ToUInt64();
        }

        return SIPAddress(IPv4[0], IPv4[1], IPv4[2], IPv4[3]);
    }    
}

SIPAddress::SIPAddress()
{
    memset(&IP, 0, sizeof(IP));
}

// SIPAddress::SIPAddress(const CString &InIP)
// {
//     *this = InIP;
// }

SIPAddress::SIPAddress(const IPAddress & InIP, bool bInIsIPv6)
{
    IP = InIP;
    bIsIPv6 = bInIsIPv6;
}

SIPAddress::SIPAddress(uint8_t InIP1, uint8_t InIP2, uint8_t InIP3, uint8_t InIP4)
{
    IP.IPv4[0] = InIP1;
    IP.IPv4[1] = InIP2;
    IP.IPv4[2] = InIP3;
    IP.IPv4[3] = InIP4;
    bIsIPv6 = false;
}

SIPAddress::SIPAddress(uint16_t InIP1, uint16_t InIP2, uint16_t InIP3, uint16_t InIP4, uint16_t InIP5, uint16_t InIP6, uint16_t InIP7, uint16_t InIP8)
{
    IP.IPv6[0] = InIP1;
    IP.IPv6[1] = InIP2;
    IP.IPv6[2] = InIP3;
    IP.IPv6[3] = InIP4;
    IP.IPv6[4] = InIP5;
    IP.IPv6[5] = InIP6;
    IP.IPv6[6] = InIP7;
    IP.IPv6[7] = InIP8;
    bIsIPv6 = true;
}

SIPAddress &SIPAddress::operator=(const CString &InIP)
{
    //check if it is ipv6
    if (InIP.Find(U':') !=-1)
    {
        bIsIPv6=true;

        //split the string
        auto IPArray = InIP.Split(U':');

        //check if it is a valid ipv6 address
        if (IPArray.Num() != 8)
        {
            //invalid ipv6 address
            memset(&IP, 0, sizeof(IP));
            return *this;
        }

        //convert to ipv6 address
        for (int i = 0; i < 8; i++)
        {
            IP.IPv6[i] = (uint16_t)IPArray[i].HexToUInt64();
        }

    }
    else
    {
        //ipv4
        bIsIPv6=false;

        //split the string
        auto IPArray = InIP.Split(U'.');
        if (IPArray.Num() != 4)
        {
            //invalid ipv4 address
            memset(&IP, 0, sizeof(IP));
            return *this;
        }

        //convert to ipv4 address
        for (int i = 0; i < 4; i++)
        {
            IP.IPv4[i] = (uint8_t)IPArray[i].ToUInt64();
        }

    }
    return *this;
}

bool SIPAddress::operator==(const SIPAddress &Other) const
{
    if(bIsIPv6!=Other.bIsIPv6)
    {
        return false;
    }

    if(bIsIPv6)
    {
        for (int i = 0; i < 8; i++)
        {
            if (IP.IPv6[i] != Other.IP.IPv6[i])
            {
                return false;
            }
        }
    }
    else
    {
        for (int i = 0; i < 4; i++)
        {
            if (IP.IPv4[i] != Other.IP.IPv4[i])
            {
                return false;
            }
        }
    }

    return true;
}

bool SIPAddress::IsValid() const
{
    if(bIsIPv6)
    {
        for (int i = 0; i < 8; i++)
        {
            if (IP.IPv6[i] != 0)
            {
                return true;
            }
        }
    }
    else
    {
        for (int i = 0; i < 4; i++)
        {
            if (IP.IPv4[i] != 0)
            {
                return true;
            }
        }
    }

    return false;
}

bool SIPAddress::IsIPv6() const
{
    return bIsIPv6;
}

CString SIPAddress::ToString() const
{
    if(bIsIPv6)
    {
        CString IPString;
        for (int i = 0; i < 8; i++)
        {
            IPString += CString::CreateHex(IP.IPv6[i], false);
            if (i != 7)
            {
                IPString += U':';
            }
        }
        return IPString;
    }
    else
    {
        CString IPString;
        for (int i = 0; i < 4; i++)
        {
            IPString += CString((uint64_t)IP.IPv4[i]);
            if (i != 3)
            {
                IPString += U'.';
            }
        }
        return IPString;    
    }
}
